#pragma once

#include "log.h"
#include "MyUnitGroup.h"
#include "MicroUnitData.h"
#include <math.h>
#include <algorithm>

using namespace std;
namespace scai {
	typedef std::vector<BWAPI::Unit*> Units;

	struct PotentialField {
	
		BWAPI::Unit* unit;
		BWAPI::Position* pos;
		MicroUnitData * data;
		std::vector<double> weights;

		bool attractive;

		PotentialField(BWAPI::Unit * unit, MicroUnitData * data, std::vector<double> weights): unit(unit), data(data), weights(weights) {}

		virtual double calcRelForce() =0;

		virtual double* getForceVector() {
			double* vector = new double[2];
			double force = calcRelForce();
			int relX = pos->x() - unit->getPosition().x();
			int relY = pos->y() - unit->getPosition().y();
			vector[0] = force * (relX/(double)(relX + relY));
			vector[1] = force * (relY/(double)(relX + relY));
			if (!attractive) {
				vector[0] *= -1;
				vector[1] *= -1;
			}
			return vector;
		}

		virtual double* sumVector(double *otherVector) {
			double* vector = getForceVector();
			vector[0] = vector[0] + otherVector[0];
			vector[1] = vector[1] + otherVector[1];
			return vector;
		}
	};

	struct MaximumShootingDistance : public PotentialField {
		BWAPI::Unit* target;

		MaximumShootingDistance(BWAPI::Unit * unit, BWAPI::Unit* target, MicroUnitData * data, std::vector<double> weights) : 
			PotentialField(unit,data,weights), target(target) {
			pos = new BWAPI::Position(target->getPosition());
		}

		double calcRelForce() {
			BWAPI::Position &p = unit->getPosition();
			double distance = pos->getDistance(p);
			int mMSD = unit->getType().groundWeapon().maxRange();
			int eMSD = target->getType().groundWeapon().maxRange();
			return weights.at(3) * (1 - (target->getHitPoints()/target->getType().maxHitPoints())) + weights.at(4) * (1/distance);
		}
	};

	struct EscapeUnitField : public PotentialField {
		BWAPI::Unit* target;
		int threshold;

		EscapeUnitField(BWAPI::Unit* unit, MicroUnitData * data, std::vector<double> weights, BWAPI::Unit* target, int t) : 
			PotentialField(unit,data,weights), target(target), threshold(t) {
				pos = new BWAPI::Position(target->getPosition());
		}

		double calcRelForce() {
			int distance = unit->getPosition().getApproxDistance(target->getPosition());
			int MSD = max(unit->getType().groundWeapon().maxRange(), target->getType().groundWeapon().maxRange());

			if (distance - (MSD + weights.at(2) * (1 - (unit->getHitPoints()/unit->getType().maxHitPoints()))) > 0) {
				attractive = true;
				return weights.at(0) * distance;
			}
			else {
				attractive = false;
				return weights.at(1) / (double)distance;
			}
		}
	};

	struct CenterOfSquad : public PotentialField {
		CenterOfSquad(BWAPI::Unit* unit, MicroUnitData * data, std::vector<double> weights, std::vector<BWAPI::Position*> positions) :
			PotentialField(unit,data,weights) {
				int x = 0;
				int y = 0;

				for (int i=0; i < positions.size(); i++) {
					x += positions.at(i)->x();
					y += positions.at(i)->y();
				}
				x = x / positions.size();
				y = y / positions.size();

				pos = new BWAPI::Position(x,y);
		}
		
		double calcRelForce() {
			return weights.at(5);
		}
	};
}